package App.Utility;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;

import org.docx4j.Docx4J;
import org.docx4j.XmlUtils;
import org.docx4j.convert.in.xhtml.XHTMLImporterImpl;
import org.docx4j.convert.out.FOSettings;
import org.docx4j.dml.wordprocessingDrawing.Inline;
import org.docx4j.fonts.IdentityPlusMapper;
import org.docx4j.fonts.Mapper;
import org.docx4j.fonts.PhysicalFonts;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.io.SaveToZipFile;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.BinaryPartAbstractImage;
import org.docx4j.org.apache.poi.util.StringUtil;
import org.docx4j.wml.ContentAccessor;
import org.docx4j.wml.Drawing;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.docx4j.wml.Tbl;
import org.docx4j.wml.Tc;
import org.docx4j.wml.Text;
import org.docx4j.wml.Tr;
import org.springframework.util.StringUtils;

import com.google.gson.Gson;

//import io.swagger.models.Xml;

public class WordPrintUtils {
	private Map<String, Object> mappings;
	private WordprocessingMLPackage wordMLPackage;
	private Map<String, List<Object> > convertedHtmlItems ;
	private String token ;
	private String baseUrl;
	private HashMap<String,RichFieldInfo> richEditMap=  new HashMap<String, WordPrintUtils.RichFieldInfo>();
	public void ExportFile(Map<String, Object> data,List<HtmlFieldMapInfo>allHtmlMap, String TemplateFilePath,
							String ExportFillePath ,boolean isToPdf,String baseUrl,String token)  throws Exception {
		this.token = token;
		this.baseUrl = baseUrl;
		File templatefile = new File(TemplateFilePath);
		wordMLPackage = WordprocessingMLPackage.load(templatefile);
		Mapper fontMapper = new IdentityPlusMapper();
		fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
		fontMapper.put("宋体",PhysicalFonts.get("SimSun"));
		fontMapper.put("微软雅黑",PhysicalFonts.get("Microsoft Yahei"));
		fontMapper.put("黑体",PhysicalFonts.get("SimHei"));
		fontMapper.put("楷体",PhysicalFonts.get("KaiTi"));
		fontMapper.put("新宋体",PhysicalFonts.get("NSimSun"));
		fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
		fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
		fontMapper.put("宋体扩展",PhysicalFonts.get("simsun-extB"));
		fontMapper.put("仿宋",PhysicalFonts.get("FangSong"));
		fontMapper.put("仿宋_GB2312",PhysicalFonts.get("FangSong_GB2312"));
		fontMapper.put("幼圆",PhysicalFonts.get("YouYuan"));
		fontMapper.put("华文宋体",PhysicalFonts.get("STSong"));
		fontMapper.put("华文中宋",PhysicalFonts.get("STZhongsong"));

		wordMLPackage.setFontMapper(fontMapper);
		mappings = data;
		FileAndSetHtmlTemplate(data,allHtmlMap);
		Parse(wordMLPackage);
		if (isToPdf) {
			SaveToPdf(ExportFillePath);
		}else {
			SaveToZipFile saver = new SaveToZipFile(wordMLPackage);
			saver.save(ExportFillePath);
		}
	}
	public void ExportFile(Map<String, Object> data,List<HtmlFieldMapInfo>allHtmlMap,InputStream templateInputStream,
			String ExportFillePath ,boolean isToPdf,String baseUrl,String token)  throws Exception {
		this.token = token;
		this.baseUrl = baseUrl;
		wordMLPackage = WordprocessingMLPackage.load(templateInputStream);
		Mapper fontMapper = new IdentityPlusMapper();
		fontMapper.put("隶书", PhysicalFonts.get("LiSu"));
		fontMapper.put("宋体",PhysicalFonts.get("SimSun"));
		fontMapper.put("微软雅黑",PhysicalFonts.get("Microsoft Yahei"));
		fontMapper.put("黑体",PhysicalFonts.get("SimHei"));
		fontMapper.put("楷体",PhysicalFonts.get("KaiTi"));
		fontMapper.put("新宋体",PhysicalFonts.get("NSimSun"));
		fontMapper.put("华文行楷", PhysicalFonts.get("STXingkai"));
		fontMapper.put("华文仿宋", PhysicalFonts.get("STFangsong"));
		fontMapper.put("宋体扩展",PhysicalFonts.get("simsun-extB"));
		fontMapper.put("仿宋",PhysicalFonts.get("FangSong"));
		fontMapper.put("仿宋_GB2312",PhysicalFonts.get("FangSong_GB2312"));
		fontMapper.put("幼圆",PhysicalFonts.get("YouYuan"));
		fontMapper.put("华文宋体",PhysicalFonts.get("STSong"));
		fontMapper.put("华文中宋",PhysicalFonts.get("STZhongsong"));
		wordMLPackage.setFontMapper(fontMapper);
		mappings = data;
		FileAndSetHtmlTemplate(data,allHtmlMap);
		Parse(wordMLPackage);
		if (isToPdf) {
			SaveToPdf(ExportFillePath);
		}else {
			SaveToZipFile saver = new SaveToZipFile(wordMLPackage);
			saver.save(ExportFillePath);
		}
	}
	private void ReConstructRichEdit(RichFieldInfo richField) {
		if (richField == null || richField.body == null || richField.body.length() == 0 ) return ;
		if (richField.images == null ) return ;
		for(RichImageFieldInfo image :richField.images) {
			String imghtml = "<img src=\""+baseUrl +"/"+image.src+"?token="+this.token+"\" ";
			imghtml = imghtml +" alt=\""+image.alt+"\" width=\"" + String.valueOf(image.getImageSize().x)+"\" ";
			imghtml = imghtml +" height=\"" +String.valueOf(image.getImageSize().y)+"\"></img>";
			
			richField.body = richField.body.replaceAll(image.ref, imghtml );
			richField.body ="<div>" + richField.body+"</div>";
		}

		if(StringUtils.isEmpty(richField.body) == false){
			richField.body ="<div>" + richField.body+"</div>";
			richField.body = HtmlUtils.setHTMLHeadForEescapeCharacter(richField.body);
		}
	}
	/***
	 * 把数据中的富文本进行处理，加入Docx4J模板内，以便用xMLConverter
	 * @throws JAXBException 
	 * @throws Docx4JException 
	 */
	private void FileAndSetHtmlTemplate(Map<String, Object> data,List<HtmlFieldMapInfo>allHtmlMap) throws JAXBException, Docx4JException {

		Gson gson = new Gson();
		HashMap<String, Object> mapHtml = new HashMap<String, Object>();
		for(HtmlFieldMapInfo item :allHtmlMap) {
			if (StringUtils.isEmpty(item.getMainfieldname())) continue;
			if (StringUtils.isEmpty(item.getSubfieldname())) {
				if (data.containsKey(item.getMainfieldname()) && 
						data.get(item.getMainfieldname()) instanceof String) {
					String tmp =(String) data.get(item.getMainfieldname());
					if(StringUtils.isEmpty(tmp))  continue;
					RichFieldInfo richInfo = gson.fromJson(tmp, RichFieldInfo.class);
					ReConstructRichEdit(richInfo);
					richEditMap.put(item.getMainfieldname(), richInfo);
					mapHtml.put(item.getMainfieldname(), richInfo.body);
				}
			}
			else
			{
				if (data.containsKey(item.getMainfieldname()) &&
						data.get(item.getMainfieldname()) instanceof List) {
					List<Map<String,Object>> list = (List<Map<String,Object>>)data.get(item.getMainfieldname());
					int index= 0 ;
					for(int i=0;i<list.size();i++) {
						Map<String,Object> subItem = list.get(i);
						if (subItem != null 
								&& subItem.containsKey(item.getSubfieldname()) 
								&& subItem.get(item.getSubfieldname()) instanceof String) {
							String tmp =(String) subItem.get(item.getSubfieldname());
							if(StringUtils.isEmpty(tmp))  continue;
							RichFieldInfo richInfo = gson.fromJson(tmp, RichFieldInfo.class);
							ReConstructRichEdit(richInfo);
							richEditMap.put(item.getMainfieldname()+"_"+i +item.getSubfieldname(), richInfo);
							mapHtml.put(item.getMainfieldname()+"_"+i +item.getSubfieldname(), richInfo.body);
						}
					}
				}
			}
		}
		String xml = XmlUtils.marshaltoString(wordMLPackage.getMainDocumentPart().getJaxbElement(), true);
		wordMLPackage.getMainDocumentPart().setJaxbElement((org.docx4j.wml.Document) XmlUtils.unmarshallFromTemplate(xml, mapHtml));

		convertedHtmlItems = new HashMap<String, List<Object>>();
		for(String key :mapHtml.keySet()) {
			String val = (String)mapHtml.get(key);
			if (!convertedHtmlItems.containsKey(val)) {
				XHTMLImporterImpl xhtmlConverter = new XHTMLImporterImpl(wordMLPackage);
				List<Object>  items=  xhtmlConverter.convert(val, null);
				convertedHtmlItems.put(val, items);
			}

		}
		xml = null;
	}

	private List<Object> createHtml2XML(RichFieldInfo richInfo){
		try {
			String content = richInfo.body;
			List<Object> ps = null ;
			if(this.convertedHtmlItems.containsKey(content))
			{
				ps =  convertedHtmlItems.get(content);
			}
			if(ps == null ) ps = new ArrayList<Object>();
			return ps;
		}catch(Exception ex) {
			return new ArrayList<Object>();
		}
	}
	private P getImageToPackage(RichImageFieldInfo fileInfo) throws Exception {
		byte [] buff = getImageFromPaas(fileInfo);
		return addImageToPackage(this.wordMLPackage, buff, fileInfo.getImageSize().x, fileInfo.getImageSize().y);
	}
	private byte[] getImageFromPaas(RichImageFieldInfo fileInfo) throws Exception {
		File file = new File("H:\\a.jpg");
		InputStream is = new FileInputStream(file);  
		   long length = file.length();  
		     if (length > Integer.MAX_VALUE) {  
		         System.out.println("File too large!!");  
		     }  
		     byte[] bytes = new byte[(int)length];  
		     int offset = 0;  
		     int numRead = 0;  
		     while (offset < bytes.length && (numRead=is.read(bytes, offset, bytes.length-offset)) >= 0) {  
		         offset += numRead;  
		     }  
		     // 确认所有的字节都没读取  
		     if (offset < bytes.length) {  
		         System.out.println("Could not completely read file " +file.getName());  
		     }  
		     is.close(); 
		     return bytes;
	}


	private void SaveToPdf(String ExportFilePath) throws Exception {


		FileOutputStream os = new java.io.FileOutputStream(ExportFilePath);

		FOSettings foSettings = Docx4J.createFOSettings();
		foSettings.setWmlPackage(wordMLPackage);
		Docx4J.toFO(foSettings, os, Docx4J.FLAG_EXPORT_PREFER_XSL);

	}
	private void Parse(WordprocessingMLPackage template) throws Exception {
		List<Object> children =  template.getMainDocumentPart().getContent();
		for(int i=0;i<children.size();) {
			Object child = children.get(i);
			if (child instanceof P) {
				int tmpStep = ParseP((P)child,children,i,this.mappings,"");
				i=i+tmpStep;

			}else if (child instanceof JAXBElement 
					&& ((JAXBElement)child).getValue() instanceof Tbl) {
				ParseTbl((Tbl)(((JAXBElement)child).getValue()));
				i++;
			}else{
				i++;
			}
		}
	}

	private int ParseP(P p ,List<Object> parentList,int index,Map<String,Object> data,String baseFieldName) throws Exception {
		if (p == null ) return 1;
		List<Object> children =  p.getContent();
		boolean isStartCheck =false;
		ReplaceStruct  CurrentReplace = null;
		for(int i=0;i<children.size();i++){
			Object child = children.get(i);
			if (!(child instanceof R)) continue;
			List<Object> texts =((R)child).getContent();
			for(int j=0;j<texts.size();j++) {
				Object subChild = texts.get(j);
				if (!(subChild instanceof JAXBElement)
						|| !((JAXBElement)subChild).getDeclaredType().equals(Text.class)
						) continue;
				String t = (String) ((Text)((JAXBElement)subChild).getValue()).getValue();
				int firstStartIndex=  t.indexOf('{');
				int firstEndIndex = t.indexOf('}');

				if (CurrentReplace != null) {
					/**
					 * 之前已经有开始标记，那么就存在以下几种情况：
					 * 1、本段有结束标记和结束标记
					 * 1.1开始标记在前
					 * 1.2结束标记在前
					 * 2、本段仅有开始标志
					 * 3、本段仅有结束标记
					 * 4、本段什么都没有
					 * 
					 */
					if (firstStartIndex>=0 && firstEndIndex >=0 ){
						if(firstEndIndex < firstStartIndex) {
							//如果终止符在前，则先把终止符的处理掉
							String tmpKey = ((Text)CurrentReplace.StartText.getValue()).getValue().substring(CurrentReplace.StartIndex+1);
							for(JAXBElement st : CurrentReplace.MiddleList) {
								tmpKey= tmpKey + ((Text)st.getValue()).getValue();
							}
							tmpKey = tmpKey+t.substring(0,firstEndIndex);
							ReplaceKeyResultInfo result  = this.CheckKey(tmpKey,data,baseFieldName,index);
							if (result!=null && result.IsMatch) {
								if (result.KeyType == 1) {
									String tmpText =((Text) CurrentReplace.StartText.getValue()).getValue();
									tmpText=tmpText.substring(0,CurrentReplace.StartIndex);
									tmpText = tmpText + result.Value;
									((Text)CurrentReplace.StartText.getValue()).setValue(tmpText);
									tmpText = t.substring(firstEndIndex+1);
									((Text)((JAXBElement)subChild).getValue()).setValue(tmpText);
									for(JAXBElement st :CurrentReplace.MiddleList) {
										((R)(((Text)st.getValue()).getParent())).getContent().remove(st);
									}
								}else if (result.KeyType == 2) {
									//这里可能是HTML替换
									String tmpText =((Text) CurrentReplace.StartText.getValue()).getValue();
									tmpText=tmpText.substring(0,CurrentReplace.StartIndex);
									//tmpText = tmpText + result.Value;
									((Text)CurrentReplace.StartText.getValue()).setValue(tmpText);
									tmpText = t.substring(firstEndIndex+1);
									((Text)((JAXBElement)subChild).getValue()).setValue(tmpText);
									for(JAXBElement st :CurrentReplace.MiddleList) {
										((R)(((Text)st.getValue()).getParent())).getContent().remove(st);
									}
									//在index之后插入html段落
									int steps = 1;
									try {

										List<?>htmlItems = createHtml2XML(result.richField);//xhtmlConverter.convert(result.Value, null) ;
										parentList.addAll(index+1,htmlItems);
										steps =steps + htmlItems.size();
									}catch(Exception ex) {

									}
									//补充剩余数据
									P remainP = XmlUtils.deepCopy(p);
									remainP.getContent().clear();
									R tmpR = XmlUtils.deepCopy((R)child);
									tmpR.getContent().clear();

									JAXBElement tmpT =  XmlUtils.deepCopy((JAXBElement)subChild);
									((Text)tmpT.getValue()).setValue(t.substring(firstEndIndex+1));//这里其实是要拆分的
									tmpR.getContent().add(tmpT);
									//把当前child的其他内容加回去
									for(int tmpj = j+1;tmpj<texts.size();tmpj++) {
										Object obj = texts.get(tmpj);
										if (!(obj instanceof JAXBElement)
												|| !((JAXBElement)obj).getDeclaredType().equals(Text.class)
												) continue;
										JAXBElement obj2=  XmlUtils.deepCopy((JAXBElement)obj);
										tmpR.getContent().add(obj2);
									}
									remainP.getContent().add(tmpR);
									for(int tmpj = texts.size()-1;tmpj>j;tmpj--) {
										((R)child).getContent().remove(tmpj);
									}
									for(int tmpj = i+1;tmpj<children.size();tmpj++)
									{
										Object obj = children.get(tmpj);
										if (!(obj instanceof R)) continue;										
										remainP.getContent().add(XmlUtils.deepCopy(obj));
									}
									for(int tmpj =children.size() -1;tmpj>=i;tmpj--)
									{
										p.getContent().remove(tmpj);
									}
									parentList.add(index+steps,remainP);
									steps++;
									//这里需要直接退出
									return steps-1;
								}
								CurrentReplace = null;//取消开始标记
							}else {
								CurrentReplace = null;//取消开始标记
							}
							firstStartIndex = -1;
						}
						firstStartIndex = t.indexOf('{');

						while(true) {
							int NextStartIndex = t.indexOf('{',firstStartIndex+1);
							int NextEndIndex = t.indexOf('}',firstStartIndex+1);
							if (NextStartIndex >0  && NextEndIndex>0 ) {

							}else if (NextStartIndex >0 ) {
								CurrentReplace = new ReplaceStruct();
								CurrentReplace.StartIndex = NextStartIndex;
								CurrentReplace.StartText = (JAXBElement)subChild;
								break;
							}else if (NextEndIndex>0) {
								String tmpKey = t.substring(firstStartIndex+1,NextEndIndex);
								ReplaceKeyResultInfo result = this.CheckKey(tmpKey,data,baseFieldName,index);
								if (result != null &&result.IsMatch) {
									if (result.KeyType == 1  ) {
										String tmpText=  t.substring(0,firstStartIndex);
										tmpText = tmpText+result.Value;
										tmpText = tmpText +t.substring(NextEndIndex+1);
										((Text)((JAXBElement)subChild).getValue()).setValue(tmpText);
										t=tmpText;
									}else if (result.KeyType == 2) {

										//这里可能是HTML替换
										String tmpText =((Text) CurrentReplace.StartText.getValue()).getValue();
										tmpText=tmpText.substring(0,CurrentReplace.StartIndex);
										//tmpText = tmpText + result.Value;
										((Text)CurrentReplace.StartText.getValue()).setValue(tmpText);
										tmpText = t.substring(firstEndIndex+1);
										((Text)((JAXBElement)subChild).getValue()).setValue(tmpText);
										for(JAXBElement st :CurrentReplace.MiddleList) {
											((R)(((Text)st.getValue()).getParent())).getContent().remove(st);
										}
										//在index之后插入html段落
										int steps = 1;
										try {

											List<?>htmlItems =  createHtml2XML(result.richField);//xhtmlConverter.convert(result.Value, null) ;
											parentList.addAll(index+1,htmlItems);
											steps =steps + htmlItems.size();
										}catch(Exception ex) {

										}
										//补充剩余数据
										P remainP = XmlUtils.deepCopy(p);
										remainP.getContent().clear();
										R tmpR = XmlUtils.deepCopy((R)child);
										tmpR.getContent().clear();

										JAXBElement tmpT =  XmlUtils.deepCopy((JAXBElement)subChild);
										((Text)tmpT.getValue()).setValue(t.substring(firstEndIndex+1));//这里其实是要拆分的
										tmpR.getContent().add(tmpT);
										//把当前child的其他内容加回去
										for(int tmpj = j+1;tmpj<texts.size();tmpj++) {
											Object obj = texts.get(tmpj);
											if (!(obj instanceof JAXBElement)
													|| !((JAXBElement)obj).getDeclaredType().equals(Text.class)
													) continue;
											JAXBElement obj2=  XmlUtils.deepCopy((JAXBElement)obj);
											tmpR.getContent().add(obj2);
										}
										for(int tmpj = texts.size()-1;tmpj>j;tmpj--) {
											((R)child).getContent().remove(tmpj);
										}
										remainP.getContent().add(tmpR);
										for(int tmpj = i+1;tmpj<children.size();tmpj++)
										{
											Object obj = children.get(tmpj);
											if (!(obj instanceof R)) continue;										
											remainP.getContent().add(XmlUtils.deepCopy(obj));
										}
										for(int tmpj =children.size() -1;tmpj>=i;tmpj--)
										{
											p.getContent().remove(tmpj);
										}
										parentList.add(index+steps,remainP);
										steps++;
										//这里需要直接退出
										return steps-1;
									}
								}
								break;
							}else {
								break;
							}

						}

					}
					else if (firstStartIndex>=0  ) {
						/**
						 * 因为没有结束节点，所以只需要找到本段的最后的开始节点，重置当前处理标记即可
						 */
						int lastStartIndex = t.lastIndexOf('{');
						CurrentReplace.StartIndex = lastStartIndex;
						CurrentReplace.StartText = (JAXBElement)subChild;
					}
					else if (firstEndIndex >=0 ) {
						/*
						 * 有结束节点，先处理结束节点，然后按没有开始标记的段来处理
						 */
						String tmpKey =((Text) CurrentReplace.StartText.getValue()).getValue().substring(CurrentReplace.StartIndex+1);
						for(JAXBElement st : CurrentReplace.MiddleList) {
							tmpKey= tmpKey +((Text)st.getValue()).getValue();
						}
						tmpKey = tmpKey+t.substring(0,firstEndIndex);
						ReplaceKeyResultInfo result  = this.CheckKey(tmpKey,data,baseFieldName,index);
						if (result!=null && result.IsMatch) {
							if (result.KeyType == 1) {
								String tmpText =( (Text)CurrentReplace.StartText.getValue()).getValue();
								tmpText=tmpText.substring(0,CurrentReplace.StartIndex);
								tmpText = tmpText + result.Value;
								((Text)CurrentReplace.StartText.getValue()).setValue(tmpText);
								tmpText = t.substring(firstEndIndex+1);
								((Text)((JAXBElement)subChild).getValue()).setValue(tmpText);
								for(JAXBElement st :CurrentReplace.MiddleList) {
									((R)(((Text)st.getValue()).getParent())).getContent().remove(st);
								}
							}else if (result.KeyType == 2) {
								//这里可能是HTML替换
								String tmpText =((Text) CurrentReplace.StartText.getValue()).getValue();
								tmpText=tmpText.substring(0,CurrentReplace.StartIndex);
								//tmpText = tmpText + result.Value;
								((Text)CurrentReplace.StartText.getValue()).setValue(tmpText);
								tmpText = t.substring(firstEndIndex+1);
								((Text)((JAXBElement)subChild).getValue()).setValue(tmpText);
								for(JAXBElement st :CurrentReplace.MiddleList) {
									((R)(((Text)st.getValue()).getParent())).getContent().remove(st);
								}
								//在index之后插入html段落
								int steps = 1;
								try {
									List<?>htmlItems =  createHtml2XML(result.richField);//xhtmlConverter.convert(result.Value, null) ;
									parentList.addAll(index+1,htmlItems);
									steps =steps + htmlItems.size();
								}catch(Exception ex) {

								}
								//补充剩余数据
								P remainP = XmlUtils.deepCopy(p);
								remainP.getContent().clear();
								R tmpR = XmlUtils.deepCopy((R)child);
								tmpR.getContent().clear();

								JAXBElement tmpT =  XmlUtils.deepCopy((JAXBElement)subChild);
								((Text)tmpT.getValue()).setValue(t.substring(firstEndIndex+1));//这里其实是要拆分的
								tmpR.getContent().add(tmpT);
								//把当前child的其他内容加回去
								for(int tmpj = j+1;tmpj<texts.size();tmpj++) {
									Object obj = texts.get(tmpj);
									if (!(obj instanceof JAXBElement)
											|| !((JAXBElement)obj).getDeclaredType().equals(Text.class)
											) continue;
									JAXBElement obj2=  XmlUtils.deepCopy((JAXBElement)obj);
									tmpR.getContent().add(obj2);
								}
								remainP.getContent().add(tmpR);
								for(int tmpj = texts.size()-1;tmpj>j;tmpj--) {
									((R)child).getContent().remove(tmpj);
								}
								for(int tmpj = i+1;tmpj<children.size();tmpj++)
								{
									Object obj = children.get(tmpj);
									if (!(obj instanceof R)) continue;										
									remainP.getContent().add(XmlUtils.deepCopy(obj));
								}
								for(int tmpj =children.size() -1;tmpj>=i;tmpj--)
								{
									p.getContent().remove(tmpj);
								}
								parentList.add(index+steps,remainP);
								steps++;
								//这里需要直接退出
								return steps-1;
							}
							CurrentReplace = null;//取消开始标记
						}else { 
							CurrentReplace = null;//取消开始标记
						}

					}else {
						/***
						 * 这种情况，表示现在还在标记内，把标志内的内容全部压入列表即可
						 */
						CurrentReplace.MiddleList.add((JAXBElement)subChild);
					}
				}else {
					/**
					 * 没有开始标记，存在以下几种亲口光
					 * 1、本段有开始标记和结束标记
					 * 1.1结束标记在前
					 * 1.2开始标记在前
					 * 2.本段仅有开始标记
					 * 3.本段仅有结束标记
					 * 4、本段什么都没有
					 * 处理规则如下：
					 * 1.1  +1.2
					 * 		这种情况非常复杂
					 * 2的处理规则
					 * 	标记开始（该处理什么就处理什么)
					 * 
					 * 	
					 */
					if(firstStartIndex>=0 && firstEndIndex >=0 ) {
						//第一种情况
						while(true) {
							int NextStartIndex = t.indexOf('{',firstStartIndex+1);
							int NextEndIndex = t.indexOf('}',firstStartIndex+1);
							if (NextStartIndex>0 && NextEndIndex >0) {
								if (NextStartIndex < NextEndIndex ) {
									firstStartIndex = NextStartIndex;
									continue;//使用后面的替换前面的开始标志
								}else {
									//到这里，就是firstStartIndex 和NextEndIndex可以组成一对,所以这里就需要替换，注意替换的长度变化
									String tmpKey = t.substring(firstStartIndex+1,NextEndIndex);
									ReplaceKeyResultInfo result  = this.CheckKey(tmpKey,data,baseFieldName,index);
									if (result != null && result.IsMatch) {
										//需要替换
										if (result.KeyType == 1 ) {
											String retText = t.substring(0,firstStartIndex);
											retText = retText + result.Value;
											int tmpNextEndIndex = retText.length()+1;
											retText = retText + t.substring(NextEndIndex +1);
											firstStartIndex = NextStartIndex + (tmpNextEndIndex - NextEndIndex);//调整下次检查的开始节点
											t = retText;
											((JAXBElement)subChild).setValue(t);
										}else if (result.KeyType == 2)  {
											String tmpText =t.substring(0,firstStartIndex);
											((Text)((JAXBElement)subChild).getValue()).setValue(tmpText);
											//在index之后插入html段落
											int steps = 1;
											try {

												List<?>htmlItems =  createHtml2XML(result.richField);//xhtmlConverter.convert(result.Value, null) ;
												parentList.addAll(index+1,htmlItems);
												steps =steps + htmlItems.size();
											}catch(Exception ex) {

											}
											//补充剩余数据
											P remainP = XmlUtils.deepCopy(p);
											remainP.getContent().clear();
											R tmpR = XmlUtils.deepCopy((R)child);
											tmpR.getContent().clear();

											JAXBElement tmpT =  XmlUtils.deepCopy((JAXBElement)subChild);
											((Text)tmpT.getValue()).setValue(t.substring(firstEndIndex+1));//这里其实是要拆分的
											tmpR.getContent().add(tmpT);
											//把当前child的其他内容加回去
											for(int tmpj = j+1;tmpj<texts.size();tmpj++) {
												Object obj = texts.get(tmpj);
												if (!(obj instanceof JAXBElement)
														|| !((JAXBElement)obj).getDeclaredType().equals(Text.class)
														) continue;
												JAXBElement obj2=  XmlUtils.deepCopy((JAXBElement)obj);
												tmpR.getContent().add(obj2);
											}
											for(int tmpj = texts.size()-1;tmpj>j;tmpj--) {
												((R)child).getContent().remove(tmpj);
											}
											for(int tmpj = i+1;tmpj<children.size();tmpj++)
											{
												Object obj = children.get(tmpj);
												if (!(obj instanceof R)) continue;										
												remainP.getContent().add(XmlUtils.deepCopy(obj));
											}
											for(int tmpj =children.size() -1;tmpj>i;tmpj--)
											{
												p.getContent().remove(tmpj);
											}
											parentList.add(index+steps,remainP);
											steps++;
											//这里需要直接退出
											return steps -1;

										}else {
											firstStartIndex = NextStartIndex;
										}
									}else {
										firstStartIndex = NextStartIndex;
									}

								}
							}else if (NextEndIndex > 0 ) {
								String tmpKey = t.substring(firstStartIndex+1,NextEndIndex);
								ReplaceKeyResultInfo result = this.CheckKey(tmpKey,data,baseFieldName,index);
								if (result != null &&result.IsMatch) {
									if(result.KeyType == 1 ) {
										String tmpText=  t.substring(0,firstStartIndex);
										tmpText = tmpText+result.Value;
										tmpText = tmpText+ t.substring(NextEndIndex+1);
										((Text)((JAXBElement)subChild).getValue()).setValue(tmpText);
										t=tmpText;
									}
									else {

										String tmpText =t.substring(0,firstStartIndex);
										((Text)((JAXBElement)subChild).getValue()).setValue(tmpText);
										//在index之后插入html段落
										int steps = 1;
										try {

											List<?>htmlItems = createHtml2XML(result.richField);// xhtmlConverter.convert(result.Value, null) ;
											parentList.addAll(index+1,htmlItems);
											steps =steps + htmlItems.size();
										}catch(Exception ex) {

										}
										//补充剩余数据
										P remainP = XmlUtils.deepCopy(p);
										remainP.getContent().clear();
										R tmpR = XmlUtils.deepCopy((R)child);
										tmpR.getContent().clear();

										JAXBElement tmpT =  XmlUtils.deepCopy((JAXBElement)subChild);
										((Text)tmpT.getValue()).setValue(t.substring(firstEndIndex+1));//这里其实是要拆分的
										tmpR.getContent().add(tmpT);
										//把当前child的其他内容加回去
										for(int tmpj = j+1;tmpj<texts.size();tmpj++) {
											Object obj = texts.get(tmpj);
											if (!(obj instanceof JAXBElement)
													|| !((JAXBElement)obj).getDeclaredType().equals(Text.class)
													) continue;
											JAXBElement obj2=  XmlUtils.deepCopy((JAXBElement)obj);
											tmpR.getContent().add(obj2);
										}
										for(int tmpj = texts.size()-1;tmpj>j;tmpj--) {
											((R)child).getContent().remove(tmpj);
										}
										for(int tmpj = i+1;tmpj<children.size();tmpj++)
										{
											Object obj = children.get(tmpj);
											if (!(obj instanceof R)) continue;										
											remainP.getContent().add(XmlUtils.deepCopy(obj));
										}
										for(int tmpj =children.size() -1;tmpj>i;tmpj--)
										{
											p.getContent().remove(tmpj);
										}
										parentList.add(index+steps,remainP);
										steps++;
										//这里需要直接退出
										return steps -1;
									}
								}
								break;

							}else if (NextStartIndex > 0){
								CurrentReplace = new ReplaceStruct();
								CurrentReplace.StartIndex = NextStartIndex;
								CurrentReplace.StartText = (JAXBElement)subChild;
								break;

							}else {
								break;
							}
						}
					}else if (firstStartIndex >=0) {
						//第二种情况
						//因为之前没有开始标记，本段也没有结束标记，所以只需要找到本text的最后一个开始标记，作为以后段落的开始标记即可。
						int lastEndIndex= t.lastIndexOf('{');//这个值肯定是有的
						CurrentReplace = new ReplaceStruct();
						CurrentReplace.StartIndex = lastEndIndex;
						CurrentReplace.StartText =(JAXBElement) subChild;

					}else if (firstEndIndex >=0 ) {
						//第三种情况
						//因为没有开始标记（包括在这个text之前和本Text内都没有开始标记），所以什么都可以不干
					}else {
						//第四种情况
						//什么都可以不干
					}
				}

			}

		}
		return 1;

	}
	private void ParseTbl(Tbl t) throws Exception {
		boolean isLoopBegin =false;
		List<Object> children = t.getContent();
		List<Object> LoopRows = new ArrayList<Object>();
		String realLoopFieldName = "";
		for(int i = 0;i<children.size();i++) {
			Object c = children.get(i);
			if (!(c instanceof Tr)) continue;
			List<Object> trs = ((Tr)c).getContent();
			if (isLoopBegin) {
				/**
				 * 如果开始了，就要检查
				 */
				String LoopFieldName = CheckIsLoopBegin((Tr)c);
				if (LoopFieldName != null ) throw(new Exception("不能使用嵌套循环"));
				if (CheckIsLoopEnd((Tr)c)) {
					//如果是结束，则需要处理循环
					isLoopBegin = false;
					t.getContent().remove(i);
					i--;
					if (mappings.containsKey(realLoopFieldName)
							&& mappings.get(realLoopFieldName) instanceof List) {
						i = i + DealLoopRows(children,i,realLoopFieldName,LoopRows,(List<Map<String, Object>>)mappings.get(realLoopFieldName));
					}
					LoopRows = new ArrayList<Object>();
				}else {
					LoopRows.add(c);
					t.getContent().remove(i);
					i--;
				}

			}else {
				/*
				 * 如果没有开始，则处理
				 */
				String LoopFields = CheckIsLoopBegin((Tr)c);
				if(LoopFields!= null) {
					realLoopFieldName  = LoopFields;
					isLoopBegin = true;
					t.getContent().remove(i);
					i--;
				}else {
					/**
					 * 正常处理
					 */
					DealOneRow((Tr)c,this.mappings,"");
				}
			}
		}
	}

	private void DealOneRow(Tr tr,Map<String,Object> data,String baseFieldName) throws Exception {
		if (tr == null ) return  ;
		List<Object> tclist = tr.getContent();
		for(Object tcc :tclist) {
			if (!(tcc instanceof JAXBElement 
					&& ((JAXBElement)tcc).getValue() instanceof Tc)) continue;
			Tc tc = (Tc)((JAXBElement)tcc).getValue() ;
			DealWithTC(tc,data,baseFieldName);
		}
		return  ;
	}
	private void DealWithTC(Tc tc,Map<String,Object> data,String baseFieldName) throws Exception {
		List<Object> list = tc.getContent();

		for(int i=0;i<list.size();i++) {
			Object obj = list.get(i);
			if (obj instanceof P) {
				ParseP((P)obj,list,i,data,baseFieldName);
			}else if(obj instanceof JAXBElement
					&& ((JAXBElement)obj).getValue() instanceof Tbl){
				DealWithInnerTable((Tbl)((JAXBElement)obj).getValue(), data, baseFieldName);
			}
		}
	}
	private void DealWithInnerTable(Tbl table,Map<String, Object> data ,String baseFieldName) throws Exception {
		List<Object> trs =  table.getContent();
		for(Object obj :trs) {
			if (obj instanceof Tr) {
				Tr tr = (Tr)obj;
				DealOneRow(tr, data, baseFieldName);
			}
		}
	}
	private int DealLoopRows(List<Object> parentList,
			int position,
			String fieldName,
			List<Object> LoopRows ,
			List<Map<String, Object>> dataList ) throws Exception {
		if (dataList == null ||dataList.size() == 0 ) {
			return 0;
		}
		int steps =0;
		for(Map<String, Object> data :dataList) {
			for(Object loopRow :LoopRows) {
				if (!(loopRow instanceof Tr))continue;
				Tr newTr = XmlUtils.deepCopy((Tr)loopRow);
				DealOneRow(newTr,data,fieldName);
				parentList.add(position+steps+1,newTr);
			}
		}
		return steps;
	}
	private String CheckIsLoopBegin(Tr tr ) throws Exception {
		if (tr == null ) return null;
		List<Object> tclist = tr.getContent();
		for(Object tcc :tclist) {
			if (!(tcc instanceof JAXBElement 
					&& ((JAXBElement)tcc).getValue() instanceof Tc)) continue;
			Tc tc = (Tc)((JAXBElement)tcc).getValue() ;
			String content = getAllTextFromTc(tc);
			content = content.trim().toLowerCase().replaceAll(" ", "");
			if (content.startsWith("loop({")) {
				content = content.substring("loop({".length());
				int tmpi = content.indexOf('}');
				if (tmpi < 0 ) throw(new Exception("语法错误(loop({变量名称})") );
				return content.substring(0, tmpi);
			}
		}
		return null;

	}
	private boolean CheckIsLoopEnd(Tr tr ) {
		if (tr == null ) return false;
		List<Object> tclist = tr.getContent();
		for(Object tcc :tclist) {
			if (!(tcc instanceof JAXBElement 
					&& ((JAXBElement)tcc).getValue() instanceof Tc)) continue;
			Tc tc = (Tc)((JAXBElement)tcc).getValue() ;
			String content = getAllTextFromTc(tc);
			content = content.trim().toLowerCase().replaceAll(" ", "");
			if (content.startsWith("endloop")) {
				return true;
			}
		}
		return false;
	}
	private String getAllTextFromTc(Tc tc) {
		if (tc == null) return ""; 
		List<Object> list =  tc.getContent();
		if (list == null )return "";
		String retStr = "";
		for(Object obj :list) {
			if (!(obj instanceof P))continue;
			retStr = retStr + this.getAllTextFromP((P)obj);
		}
		return retStr;

	}
	private String getAllTextFromP(P p) {
		if(p == null) return "";
		List<Object> list =  p.getContent();
		if (list == null )return "";
		String retStr=  "";
		for(Object obj :list) {
			if (!(obj instanceof R))continue;
			R  r = (R)obj;
			retStr= retStr+ getTextFromR(r);
		}
		return retStr;
	}
	private String getTextFromR(R r) {
		if (r == null) return "";
		List<Object> list=  r.getContent();
		if (list== null) return "";
		String retStr = "";
		for(Object obj :list) {
			if (!(obj instanceof JAXBElement 
					&& ((JAXBElement)obj).getValue() instanceof Text) )continue;
			Text t = (Text) ((JAXBElement)obj).getValue();
			retStr =retStr + t.getValue();
		}
		return retStr ;
	} 
	private ReplaceKeyResultInfo CheckKey(String orgKey,Map<String, Object> data,String baseFieldName,int index) throws Exception {
		ReplaceKeyResultInfo ret = new ReplaceKeyResultInfo();
		ret.OrgKey = orgKey;
		orgKey = orgKey.trim();
		ret.KeyType = 1;
		if (orgKey.startsWith("*")) {
			orgKey = orgKey.substring(1).trim();
			ret.KeyType =2;
			ret.RealKey =orgKey;
		}else {
			ret.KeyType = 1;
			ret.RealKey = orgKey;
		}
		String [] keys = ret.RealKey.split("\\.");
		if (keys.length == 1 ) {
			if (data.containsKey(ret.RealKey)) {
				ret.IsMatch = true;
				ret.Value =(String) data.get(ret.RealKey);
			}
			if (ret.KeyType == 2) {
				String realKey =ret.RealKey;
				if (richEditMap.containsKey(realKey)) {
					ret.richField = richEditMap.get(realKey);
				}
			}
		}else if (keys.length ==2 ) {
			if (keys[0].equalsIgnoreCase(baseFieldName)) {
				if (data.containsKey(keys[1])) {
					ret.IsMatch = true;
					ret.Value =(String) data.get(keys[1]);
				}
			}else {
				if (this.mappings.containsKey(keys[0])
						&& this.mappings.get(keys[0]) != null 
						&& this.mappings.get(keys[0]) instanceof Map) {
					Map<String, Object> detailData = (Map<String, Object>) this.mappings.get(keys[0]);
					if (detailData.containsKey(keys[1])) {
						ret.IsMatch = true;
						ret.Value = (String) detailData.get(keys[1]);
					}
					if (ret.KeyType == 2) {
						String realKey = baseFieldName +"_"+String.valueOf(index) +"_"+keys[1];
						if (richEditMap.containsKey(realKey)) {
							ret.richField = richEditMap.get(realKey);
						}
					}
				}
			}
		}else {
			throw(new Exception("字段配置异常（"+orgKey+"）"));
		}
		if(ret.Value == null) ret.Value = "";
		return ret;
	}
	private static List<Object> getAllElementFromObject(Object obj, Class<?> toSearch) {
		List<Object> result = new ArrayList<Object>();
		if (obj instanceof JAXBElement) obj = ((JAXBElement<?>) obj).getValue();

		if (obj.getClass().equals(toSearch))
			result.add(obj);
		else if (obj instanceof ContentAccessor) {
			List<?> children = ((ContentAccessor) obj).getContent();
			for (Object child : children) {
				result.addAll(getAllElementFromObject(child, toSearch));
			}

		}
		return result;
	}
	private int findPlaceHolder(String placeholder, WordprocessingMLPackage template) {
		int index = 0;

		List<Object> paragraphs = getAllElementFromObject(template.getMainDocumentPart(), P.class);

		P toReplace = null;
		for (Object p : paragraphs) {
			List<Object> texts = getAllElementFromObject(p, Text.class);
			for (Object t : texts) {
				Text content = (Text) t;
				if (content.getValue().contains(placeholder)) {
					toReplace = (P) p;
					index = template.getMainDocumentPart().getContent().indexOf(toReplace);
					break;
				}
			}
		}

		if ( toReplace != null ) {
			((ContentAccessor)toReplace.getParent()).getContent().remove(toReplace);    
		}
		return index;
	}

	private P addImageToPackage(WordprocessingMLPackage wordMLPackage,  
			byte[] bytes,int width,int height) throws Exception {  
		BinaryPartAbstractImage imagePart = BinaryPartAbstractImage.createImagePart(wordMLPackage, bytes);  

		int docPrId = 1;  
		int cNvPrId = 2;  
		Inline inline = imagePart.createImageInline("Filename hint","Alternative text", docPrId, cNvPrId,width*9525,height*9525,false);  

		P paragraph = addInlineImageToParagraph(inline);  

		return paragraph;
	}
	private P addInlineImageToParagraph(Inline inline) {  
		// 添加内联对象到一个段落中  
		ObjectFactory factory = new ObjectFactory();  
		P paragraph = factory.createP();  
		R run = factory.createR();  
		paragraph.getContent().add(run);  
		Drawing drawing = factory.createDrawing();  
		run.getContent().add(drawing);  
		drawing.getAnchorOrInline().add(inline);  
		return paragraph;  
	}  
	public class ReplaceStruct {
		public JAXBElement StartText;
		public int StartIndex;
		public List<JAXBElement> MiddleList = new ArrayList<JAXBElement>();
		public JAXBElement EndText;
		public int EndIndex;
	}
	public class ReplaceKeyResultInfo{
		public String OrgKey;
		public String RealKey ;
		public String Value;
		public int KeyType;//1=文本，2=html文本
		public boolean IsMatch =false;
		public RichFieldInfo richField = null;
	}
	public class RichFieldInfo {
		public String body;
		public ArrayList<RichImageFieldInfo> images;

	}
	public class RichImageFieldInfo {
		public String ref;
		public String src;
		public String pixel;
		public String alt;
		public RichImageSize imageSize = null;
		public RichImageSize getImageSize() {
			if (imageSize != null) return imageSize;
			imageSize = parseSize();
			return imageSize;
		}
		private RichImageSize  parseSize() {
			RichImageSize r= new RichImageSize();
			r.x =0;
			r.y = 0 ;
			if (pixel == null || pixel.length() ==0 ) {
				return r;
			}
			String  [] items = pixel.split("\\*");
			if (items == null ||items.length != 2 ) return r;
			try {
				r.x = Integer.valueOf(items[0]);
				r.y = Integer.valueOf(items[1]);
			}catch(Exception e) {
			}
			return r;
		}

	}
	public class RichImageSize{
		public int x;
		public int y;

	}
}
